先講解第一種最常見的方法吧~
首先我們要模擬LocalStorage做一個假的LocalStorageMock:
LocalStorageMock主要有五個東西
Record
來寫他的型別,Record<K,T>
可以紀錄所有物件裡key
與value
的型別,K是key
的型別,T是value
的型別key
取得store
裡的value
store
中,value
是unknown
但是最終會存進去string
,所以我寫as known as string
key
取得store
裡的value
,雖然我們這次不會用到,但是最好還是把所有實際上可能會用到的功能寫上去store
import { renderHook, act } from "@testing-library/react";
import { useLocalStorage } from "../src"; // 導入你的 useLocalStorage hook
// 模擬 LocalStorage
class LocalStorageMock {
store: Record<string, string> = {};
getItem(key: string) {
return this.store[key] || null;
}
setItem<T>(key: string, value: T) {
this.store[key] = value as unknown as string;
}
removeItem(key: string) {
delete this.store[key];
}
clear() {
this.store = {};
}
}
接下來我們要把這個假的LocalStorageMock
替代window
裡的真的localStorage
describe("useLocalStorage", () => {
let localStorageMock: LocalStorageMock;
beforeEach(() => {
localStorageMock.clear();
});
beforeAll(() => {
localStorageMock = new LocalStorageMock();
Object.defineProperty(window, "localStorage", {
value: localStorageMock,
});
});
這邊我們用到兩個新的東西,beforeEach
、beforeAll
beforeAll
:所在區域內會第一個執行。beforeEach
:每一次的測試前會先執行。順便補充另外兩個
afterAll
:所在區域內最後一個執行。afterEach
:每一次的測試後會馬上執行。區域是指如果放在describe 裡面,像block scope
一樣,describe裡面就是區域
每一次測試是指it
或test
(雖然我們沒用到test
,但他其實跟it
差不多)
// 這行意思是將全域window的localStorage的內容改成localStorageMock
Object.defineProperty(window, "localStorage", {
value: localStorageMock,
});
接下來我們要寫測試了
it("should retrieve the default value when local storage is empty", () => {
const { result } = renderHook(() => useLocalStorage("test", "default"));
expect(result.current[0]).toBe("default");
});
it("Initial state is a callback function", () => {
const { result } = renderHook(() => useLocalStorage("key", () => "value"));
expect(result.current[0]).toBe("value");
});
it("Initial state is an array", () => {
const { result } = renderHook(() => useLocalStorage("digits", [1, 2]));
expect(result.current[0]).toEqual([1, 2]);
});
it("should retrieve the stored value from local storage", () => {
localStorageMock.setItem("test", "storedValue");
const { result } = renderHook(() => useLocalStorage("test", "default"));
expect(result.current[0]).toBe("storedValue");
});
it("should set a new value", () => {
const { result } = renderHook(() => useLocalStorage("test", "default"));
act(() => {
result.current[1]("new_value");
});
expect(result.current[0]).toBe("new_value");
expect(JSON.parse(localStorageMock.getItem("test")!)).toBe("new_value");
});
it("should handle functions as new value", () => {
const { result } = renderHook(() => useLocalStorage("count", 1));
act(() => {
result.current[1]((prev) => prev + 1);
});
expect(result.current[0]).toBe(2);
expect(JSON.parse(localStorageMock.getItem("count")!)).toBe("2");
});
當我們寫完之後,就會發現test發生錯誤拉
這就是為什麼我們要寫測試拉
究竟是為什麼呢,就留給明天再說吧,自己也可以練習看看喔
如果自己寫完全沒有出錯的人,代表你很棒喔,測試就是為了怕在寫function時遇到沒預期的錯誤
今天的完整程式碼
import { renderHook, act } from "@testing-library/react";
import { useLocalStorage } from "../src"; // 導入你的 useLocalStorage hook
// 模擬 LocalStorage
class LocalStorageMock {
store: Record<string, string> = {};
getItem(key: string) {
return this.store[key] || null;
}
setItem<T>(key: string, value: T) {
this.store[key] = JSON.stringify(value);
}
removeItem(key: string) {
delete this.store[key];
}
clear() {
this.store = {};
}
}
describe("useLocalStorage", () => {
let localStorageMock: LocalStorageMock;
beforeAll(() => {
localStorageMock = new LocalStorageMock();
Object.defineProperty(window, "localStorage", {
value: localStorageMock,
});
});
beforeEach(() => {
localStorageMock.clear();
});
it("should retrieve the default value when local storage is empty", () => {
const { result } = renderHook(() => useLocalStorage("test", "default"));
expect(result.current[0]).toBe("default");
});
it("Initial state is a callback function", () => {
const { result } = renderHook(() => useLocalStorage("key", () => "value"));
expect(result.current[0]).toBe("value");
});
it("Initial state is an array", () => {
const { result } = renderHook(() => useLocalStorage("digits", [1, 2]));
expect(result.current[0]).toEqual([1, 2]);
});
it("should retrieve the stored value from local storage", () => {
localStorageMock.setItem("test", "storedValue");
const { result } = renderHook(() => useLocalStorage("test", "default"));
expect(result.current[0]).toBe("storedValue");
});
it("should set a new value", () => {
const { result } = renderHook(() => useLocalStorage("test", "default"));
act(() => {
result.current[1]("new_value");
});
expect(result.current[0]).toBe("new_value");
expect(JSON.parse(localStorageMock.getItem("test")!)).toBe("new_value");
});
it("should handle functions as new value", () => {
const { result } = renderHook(() => useLocalStorage("test", "default"));
act(() => {
result.current[1]((prev) => "new_" + prev);
});
expect(result.current[0]).toBe("new_default");
expect(JSON.parse(localStorageMock.getItem("test")!)).toBe("new_default");
});
});
抱歉今天這麼晚才出產,最近工作有點忙,我應該先提前存好文章的🥲